#!/usr/bin/env lua

----- Coding: UTF-8 ---
-- =====================================================================
-- COPYSTR: Copyright (c) 2023 By HuachenLink, All Rights Reserved.
-- FLENAME: hstr.lua
-- CONTACT: developer@huachen.link
-- CREATED: 2023-06-17 15:15:01
-- LTSVERN: 0.1
-- LTSUPDT: 2023-06-17 15:15:01
-- =====================================================================
function dump(value, desp)
  if not DEBUG or (DEBUG ~= nil and tonumber(DEBUG) < 2) then
    if desp and tonumber(desp) == 0 then
      return
    end
  end

  local nesting = 15
  local lookupTable = {}
  local result = {}
  local fmt_apdqt = "'%s'"
  local traceback = string.split(debug.traceback('', 2), '\n')
  print('dump from: ' .. string.trim(traceback[3]))

  local function _vdump(v)
    if type(v) == 'string' then
      return tostring(fmt_apdqt:format(v))
    end
    return tostring(v)
  end
  local function dump_(value, desp, indent, nest, keylen)
    desp = desp or '<var>'
    local spc = ''
    if type(keylen) == 'number' then
      spc = string.rep(' ', keylen - string.len(_vdump(desp)))
    end
    if type(value) ~= 'table' then
      result[#result + 1] = string.format('%s%s%s = %s', indent, _vdump(desp), spc, _vdump(value))
    elseif lookupTable[tostring(value)] then
      result[#result + 1] = string.format('%s%s%s = *REF*', indent, _vdump(desp), spc)
    else
      lookupTable[tostring(value)] = true
      if nest > nesting then
        result[#result + 1] = string.format('%s%s = *MAX NESTING*', indent, _vdump(desp))
      else
        result[#result + 1] = string.format('%s%s = {', indent, _vdump(desp))
        local indent2 = indent .. '    '
        local keys = {}
        local keylen = 0
        local values = {}
        for k, v in pairs(value) do
          keys[#keys + 1] = k
          local vk = _vdump(k)
          local vkl = string.len(vk)
          if vkl > keylen then
            keylen = vkl
          end
          values[k] = v
        end
        table.sort(keys, function(a, b)
          if type(a) == 'number' and type(b) == 'number' then
            return a < b
          else
            return tostring(a) < tostring(b)
          end
        end)
        for i, k in ipairs(keys) do
          dump_(values[k], k, indent2, nest + 1, keylen)
        end
        result[#result + 1] = string.format('%s}', indent)
      end
    end
  end
  dump_(value, desp, '- ', 1)

  for i, line in ipairs(result) do
    print(line)
  end
end

function string.split(istr, delim)
  if istr and 'string' == type(istr) and -- 
  delim and delim ~= '' and 'string' == type(delim) then
    local res, start, start_pos, end_pos = {}, 1, 1, 1
    while true do
      start_pos, end_pos = istr:find(delim, start, true)
      if not start_pos then
        break
      end
      table.insert(res, istr:sub(start, start_pos - 1))
      start = end_pos + 1
    end
    table.insert(res, istr:sub(start))
    return res
  end
  return {}
end
--
function string.ltrim(istr)
  return istr:gsub('^[ \t\n\r]+', '')
end
--
function string.rtrim(istr)
  return istr:gsub('[ \t\n\r]+$', '')
end
--
function string.trim(istr)
  istr = istr:gsub('^[ \t\n\r]+', '')
  return istr:gsub('[ \t\n\r]+$', '')
end
--
function string.captialize(istr)
  return istr:sub(1, 1):upper() .. istr:sub(2):lower()
end
--
function string.requal(istr, ipat)
  if istr and 'string' == type(istr) and ipat and 'string' == type(ipat) then
    for spat in ipat:gmatch('[^|]+') do
      if istr == spat then
        return true
      end
    end
  end
  return false
end
--
function string.rmatch(istr, ipat)
  if istr and 'string' == type(istr) and ipat and 'string' == type(ipat) then
    for spat in ipat:gmatch('[^|]+') do
      if istr:find(spat) then
        return true
      end
    end
  end
  return false
end
-- 
function string.contains(istr, ipat)
  -- if istr and 'string' == type(istr) and ipat and 'string' == type(ipat) then
  --   for spat in ipat:gmatch('[^|]+') do
  --     if istr:find(spat) then
  --       return true
  --     end
  --   end
  -- end
  return string.rmatch(istr, ipat)
end
--
function string.beginsWith(istr, ipat)
  if istr and 'string' == type(istr) and ipat and 'string' == type(ipat) then
    for spat in ipat:gmatch('[^|]+') do
      if string.find(istr, '^' .. spat) then
        return true
      end
    end
  end
  return false
end
--
function string.endsWith(istr, ipat)
  if istr and 'string' == type(istr) and ipat and 'string' == type(ipat) then
    for spat in ipat:gmatch('[^|]+') do
      if istr:find(spat .. '$') then
        return true
      end
    end
  end
  return false
end
--
function string.createcfg(icfgname)
  if icfgname and 'string' == type(icfgname) then
    local absp_cfg = '/hclink/run/etc/config/' .. icfgname
    local file = io.open(absp_cfg, 'a+')
    if file then
      io.close(file)
    end
  end
end
--
function string.hclexec(scmd, dlv)
  dlv = dlv or DEBUG
  if dlv and tonumber(dlv) > 1 then
    print('[<<HCLEXEC>>] ' .. scmd)
  end
  os.execute(scmd .. ' 2>/dev/null')
end
--
function string.getresult(icmd)
  if icmd and "" ~= icmd then
    local fin = io.popen(icmd .. ' 2>/dev/null')
    local rstr = fin:read('*all')
    io.close(fin)
    return rstr
  end
  return nil
end
--
function string.get_atres(icmd, devpath)
  if icmd and "" ~= icmd then
    local FMT = '/usr/sbin/atcmdtool -d %s "%s" 2>/dev/null'
    local fin = io.popen(FMT:format(devpath or '/dev/ttyCTL', icmd))
    local rstr = fin:read('*all')
    io.close(fin)
    return rstr
  end
  return nil
end
--
function string.arrayresult(scmd, delim)
  return string.getresult(tostring(scmd)):sub(2):split(delim or '@')
end
--
function string.fmtresult(scmd, ...)
  local t = string.format(tostring(scmd), ...)
  if DEBUG and tonumber(DEBUG) > 1 then
    print('[<<HCLEXEC>>] ' .. t)
  end
  return string.getresult(t)
end
--
function string.fmtexec(scmd, ...)
  return string.hclexec(string.format(tostring(scmd), ...))
end
--
function string.exists(ipath)
  local brt, file = false, io.open(ipath, 'r')
  if file then
    brt = true
    io.close(file)
  end
  return brt
end
--
function string.readfile(ipath)
  local file = io.open(ipath, 'r')
  if file then
    local content = file:read('*a')
    io.close(file)
    return content
  end
  return nil
end
--
function string.writefile(ipath, scont, imode)
  local brt, file = false, io.open(ipath, (imode or 'w+b'))
  if file then
    if file:write(scont) then
      brt = true
    end
    file:close()
  end
  return brt
end
--
-- function string.pathinfo(ipath)
--   local pos = ipath:len()
--   local extpos = pos + 1
--   while pos > 0 do
--     local b = ipath:byte(pos)
--     if b == 46 then -- 46 = char "."
--       extpos = pos
--     elseif b == 47 then -- 47 = char "/"
--       break
--     end
--     pos = pos - 1
--   end

--   local dirname = ipath:sub(1, pos)
--   local filename = ipath:sub(pos + 1)
--   extpos = extpos - pos
--   local basename = filename:sub(1, extpos - 1)
--   local extname = filename:sub(extpos)
--   return {
--     dirname = dirname,
--     filename = filename,
--     basename = basename,
--     extname = extname,
--   }
-- end
--
function string.filesize(ipath)
  local size = false
  local file = io.open(ipath, 'r')
  if file then
    local current = file:seek()
    size = file:seek('end')
    file:seek('set', current)
    io.close(file)
  end
  return size
end
--
function string.tosortedarr(istr, delim)
  local tary = istr:split(delim or ',')
  table.sort(tary)
  return tary
end
--
function string.dec2hex(sdec)
  if sdec then
    local a = tonumber(sdec)
    if a then
      return string.format("%X", a)
    end
  end
  return nil
end
--
function string.hex2dec(shex)
  if shex then
    if 'string' == type(shex) and not shex:rmatch('0[xX]') then
      shex = '0x' .. shex
    end
    return tonumber(shex, 10)
  end
  return nil
end
-- ///////////////// wrapper for io.xxxxx /////////////////
function io.exists(scmd)
  return scmd:exists()
end

--
function io.readfile(scmd)
  return scmd:readfile()
end

--
function io.pathinfo(scmd)
  return scmd:pathinfo()
end

--
function io.writefile(scmd)
  return scmd:writefile()
end

--
function io.filesize(scmd)
  return scmd:filesize()
end

--
function io.cmdres(scmd)
  return scmd:getresult()
end

--[[
------------------------------------------------------------
function _send_atcmd(atcmd, dbglv)
  local data = {}
  local res = -1
  local err = ''
  local at_res = ''

  local _resp = atcmd:get_atres(port_name)
  if _resp ~= nil then
    data = _resp:split('\n')
    if data[#data] == '' then
      data[#data] = nil
    end
    at_res = data[#data]
    res = 0
    dump(data, dbglv or 0)
  end
  return res, data, at_res, err
end

function queryxxx(req, msg)
  local retobj = {
    ret='',
    data='',
  }
  local res, data, at_res, err = _send_atcmd(msg)
  retobj['ret'] = at_res
  if data ~= nil and #data > 0 then
    retobj['data'] = table.concat(data, "@")
  end
  ubus.reply(req, {
    result=retobj,
  })
end
]]
